#pragma once

#include "helper.h"

// Disable warnings about invalid XML
#pragma warning(disable:4635)
#include <Awesomium/JSValue.h>
#pragma warning(default:4635)

using namespace Impl;
using namespace System::Collections::Generic;
using namespace System::Collections::ObjectModel;

namespace AwesomiumDotNet
{

	/// <summary>
	/// JSValue is a class that represents a Javascript value. It can be 
	/// initialized from and converted to several types: boolean, integer, 
	/// double, and string
	/// </summary>
	public ref class JSValue
	{
	private:
		Object^ value;

	internal:
		JSValue::JSValue(const Awesomium::JSValue& nativeValue);
		Awesomium::JSValue ToNative();

	public:
		/// <summary>Creates a null JSValue.</summary>
		JSValue();

		/// <summary>Creates a JSValue initialized with a boolean.</summary>
		JSValue(bool value);

		/// <summary>Creates a JSValue initialized with an integer.</summary>
		JSValue(int value);

		/// <summary>Creates a JSValue initialized with a double.</summary>
		JSValue(double value);

		/// <summary>Creates a JSValue initialized with a string.</summary>
		JSValue(String^ value);

		/// <summary>Creates a JSValue initialized with an array.</summary>
		JSValue(IEnumerable<JSValue^>^ value);

		/// <summary>Creates a JSValue initialized with an object.</summary>
		JSValue(IDictionary<String^, JSValue^>^ value);


		/// <summary>Whether or not this JSValue is a boolean.</summary>
		property bool IsBoolean { bool get(); }

		/// <summary>Returns whether or not this JSValue is an integer.</summary>
		property bool IsInteger { bool get(); }

		/// <summary>Returns whether or not this JSValue is a double.</summary>
		property bool IsDouble { bool get(); }

		/// <summary>Returns whether or not this JSValue is a string.</summary>
		property bool IsString { bool get(); }

		/// <summary>Returns whether or not this JSValue is null.</summary>
		property bool IsNull { bool get(); }

		/// <summary>Returns whether or not this JSValue is an array.</summary>
		property bool IsArray { bool get(); }

		/// <summary>Returns whether or not this JSValue is an object.</summary>
		property bool IsObject { bool get(); }


		/// <summary>Returns this JSValue as a string (converting if necessary).</summary>
		virtual String^ ToString() override;

		/// <summary>Returns this JSValue as a boolean.</summary>
		bool ToBoolean();

		/// <summary>Returns this JSValue as a double.</summary>
		double ToDouble();

		/// <summary>Returns this JSValue as an integer.</summary>
		int ToInteger();

		/// <summary>Gets this JSValue's array value</summary>
		IEnumerable<JSValue^>^ GetArray();

		/// <summary>Gets this JSValue's object value</summary>
		IDictionary<String^, JSValue^>^ GetObject();
	};


	/// <summary>
	/// FutureJSValue is a special wrapper around a JSValue that allows
	/// asynchronous retrieval of the actual value at a later time.
	/// If you are unfamiliar with the concept of a 'Future', please see:
	/// http://en.wikipedia.org/wiki/Futures_and_promises
	/// </summary>

	public ref class FutureJSValue
	{
	private:
		Awesomium::FutureJSValue* futureJSValue;

	internal:
		FutureJSValue::FutureJSValue(const Awesomium::FutureJSValue& futureJSValue)
			: futureJSValue(new Awesomium::FutureJSValue(futureJSValue))
		{
		} 

	public:
		~FutureJSValue()
		{
			FutureJSValue::!FutureJSValue();
		}

		/// <summary>
		/// Retrieve the value now. If the value has not been computed,
		/// this will block the calling thread until it has.
		/// </summary>
		JSValue^ Get();

		/// <summary>
		/// Retrieve the value now. If the value has not been computed,
		/// this will block the calling thread until it has or until the
		/// timeout is reached.
		/// </summary>
		/// <param name="timeoutMs">
		/// The maximum time to wait, in milliseconds.
		/// </param>
		JSValue^ GetWithTimeout(int timeoutMs);

	protected:
		!FutureJSValue()
		{
			delete futureJSValue;
		}
	};
}
